package categories

import (
	"context"
	"errors"
	"sort"
)

type CategoryService interface {
	GetCategories(ctx context.Context) ([]*CategoryResponse, error)
	CreateCategory(ctx context.Context, id string, name string, createAt int64, parentId string) error
	GetChatroomCategoryIds(ctx context.Context, chatroomId string) ([]string, error)
}

type defaultCategoryService struct {
	repo CategoryRepository
}

func NewCategoryService(repo CategoryRepository) CategoryService {
	return &defaultCategoryService{repo: repo}
}

func (s *defaultCategoryService) GetChatroomCategoryIds(_ context.Context, categoryId string) ([]string, error) {
	categoriesMap, _, err := s.restore()
	if err != nil {
		return nil, errors.New("获取群分类出错")
	}
	parentIds := make([]string, 0)
	parentIds = append(parentIds, categoryId)
	c, exists := categoriesMap[categoryId]
	if !exists {
		return parentIds, nil
	}
	p := c
	for len(p.ParentId) > 0 {
		var exists bool
		p, exists = categoriesMap[p.ParentId]
		if !exists {
			break
		}
		parentIds = append(parentIds, p.Id)
	}
	parentIds = Reverse(parentIds)
	return parentIds, nil
}

func Reverse(s []string) []string {
	for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
		s[i], s[j] = s[j], s[i]
	}
	return s
}

func (s *defaultCategoryService) CreateCategory(ctx context.Context, id string, name string, createAt int64, parentId string) error {
	category := Category{
		Id:              id,
		Name:            name,
		ParentId:        parentId,
		CreateTimestamp: createAt,
	}
	if err := s.repo.Save(ctx, category); err != nil {
		return err
	}
	return nil
}

func (s *defaultCategoryService) GetCategories(_ context.Context) ([]*CategoryResponse, error) {
	categoriesMap, categories, err := s.restore()
	if err != nil {
		return nil, errors.New("获取群分类出错")
	}
	s.sortCategories(categories)
	catsResp := s.CategoriesResponse(categories, categoriesMap)
	return catsResp, nil
}

func (s *defaultCategoryService) restore() (map[string]*Category, []*Category, error) {
	categories, err := s.repo.GetAll(context.Background())
	if err != nil {
		return nil, nil, errors.New("error in internet")
	}
	categoriesMap := make(map[string]*Category)
	for _, category := range categories {
		categoriesMap[category.Id] = category
	}
	categories = make([]*Category, 0)
	for _, category := range categoriesMap {
		if len(category.ParentId) != 0 {
			parent, exists := categoriesMap[category.ParentId]
			if !exists {
				continue
			}
			category.Parent = parent
			category.Parent.Children = append(category.Parent.Children, category)
		} else {
			categories = append(categories, category)
		}
	}
	return categoriesMap, categories, nil
}

func (s *defaultCategoryService) sortCategories(categories []*Category) {
	sort.SliceStable(categories, func(i, j int) bool {
		return categories[i].CreateTimestamp < categories[j].CreateTimestamp
	})
	for _, category := range categories {
		s.sortCategories(category.Children)
	}
}

func (s *defaultCategoryService) CategoriesResponse(categories []*Category, categoriesMap map[string]*Category) []*CategoryResponse {
	var result []*CategoryResponse
	for _, category := range categories {
		category, err := s.categoryToResponse(category, categoriesMap)
		if err == nil {
			result = append(result, category)
		}
	}
	return result
}

func (s *defaultCategoryService) categoryToResponse(category *Category, categoriesMap map[string]*Category) (*CategoryResponse, error) {
	children := make([]*CategoryResponse, 0, len(category.Children))
	for _, child := range category.Children {
		child, err := s.categoryToResponse(child, categoriesMap)
		if err == nil {
			children = append(children, child)
		}
	}
	parentIds, err := category.ParentIds()
	if err != nil {
		return nil, err
	}
	parentNames := make([]string, len(parentIds))
	for i, id := range parentIds {
		var name string
		parent, exists := categoriesMap[id]
		if exists {
			if len(parent.Name) > 0 {
				name = parent.Name
			}
		}
		parentNames[i] = name
	}
	return &CategoryResponse{
		Category:     *category,
		ParentName:   parentNames,
		ChildrenResp: children,
	}, nil
}
